/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.bn.types;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author rpablos
 */
public class Duration {
    
    static Pattern pattern = Pattern.compile("P(?:(\\d+)(?:[.,](\\d+))?Y)?(?:(\\d+)(?:[.,](\\d+))?M)?(?:(\\d+)(?:[.,](\\d+))?D)?(?:T(?:(\\d+)(?:[.,](\\d+))?H)?(?:(\\d+)(?:[.,](\\d+))?M)?(?:(\\d+)(?:[.,](\\d+))?S)?)?");
    static Pattern patternweek = Pattern.compile("P(?:(\\d+)(?:[.,](\\d+))?W)");
    static public enum DateComponent { WEEK,YEAR,MONTH,DAY,HOUR, MIN, SEC};
    
    Integer year,month,day;
    Integer week;
    Integer hour,min,seg;
    Integer frac;
    int numberOfFracDigits;
    
    DateComponent smallestComponent;
    
    public Duration(String duration) throws IllegalArgumentException {
        Matcher matcher = pattern.matcher(duration);
        if (matcher.matches()) {
            year = (matcher.group(1) != null)?Integer.valueOf(matcher.group(1)):null;
            if (year != null) {
                updateFrac(matcher.group(2), DateComponent.YEAR);
                smallestComponent = DateComponent.YEAR;
            }
            month = (matcher.group(3) != null)?Integer.valueOf(matcher.group(3)):null;
            if (month != null) {
                updateFrac(matcher.group(4), DateComponent.MONTH);
                smallestComponent = DateComponent.MONTH;
            }
            day = (matcher.group(5) != null)?Integer.valueOf(matcher.group(5)):null;
            if (day != null) {
                updateFrac(matcher.group(6), DateComponent.DAY);
                smallestComponent = DateComponent.DAY;
            }
            hour = (matcher.group(7) != null)?Integer.valueOf(matcher.group(7)):null;
            if (hour != null) {
                updateFrac(matcher.group(8), DateComponent.HOUR);
                smallestComponent = DateComponent.HOUR;
            }
            min = (matcher.group(9) != null)?Integer.valueOf(matcher.group(9)):null;
            if (min != null) {
                updateFrac(matcher.group(10), DateComponent.MIN);
                smallestComponent = DateComponent.MIN;
            }
            seg = (matcher.group(11) != null)?Integer.valueOf(matcher.group(11)):null;
            if (seg != null) {
                updateFrac(matcher.group(12), DateComponent.SEC);
                smallestComponent = DateComponent.SEC;
            }
        }
        else
        {
            matcher = patternweek.matcher(duration);
            if (matcher.matches()) {
                week = Integer.valueOf(matcher.group(1));
                smallestComponent = DateComponent.WEEK;
                updateFrac(matcher.group(2), DateComponent.WEEK);
            }
            else {
                throw new IllegalArgumentException("No ISO 8601 duration pattern");
            }
        }
        

    }
    public Duration(Integer year, Integer month, Integer day, 
                    Integer hour, Integer min, Integer sec, Integer frac, int numberOfFracDigits) {
        this.year = year;
        this.month = month;
        this.day = day;
        this.hour = hour;
        this.min = min;
        this.seg = sec;
        this.frac = frac;
        this.numberOfFracDigits = numberOfFracDigits;
        if (this.year != null) smallestComponent = DateComponent.YEAR;
        if (this.month != null) smallestComponent = DateComponent.MONTH;
        if (this.day != null) smallestComponent = DateComponent.DAY;
        if (this.hour != null) smallestComponent = DateComponent.HOUR;
        if (this.min != null) smallestComponent = DateComponent.MIN;
        if (this.seg != null) smallestComponent = DateComponent.SEC;
        
    }
    public Duration(Integer week, Integer frac, int numberOfFracDigits) {
        this.week = week;
        this.frac = frac;
        this.numberOfFracDigits = numberOfFracDigits;
        smallestComponent = DateComponent.WEEK;
    }
    
    private void updateFrac(String fracStr, DateComponent dc) {
        if (fracStr != null) {
                if (frac != null) 
                    throw new IllegalArgumentException("Fraction part already defined");
                frac = Integer.valueOf(fracStr);
                numberOfFracDigits = fracStr.length();
            } else if ((frac != null) && (!dc.equals(smallestComponent))) {
                throw new IllegalArgumentException("Fraction part is not in the smallest value");
            }
    }

    public DateComponent getSmallestComponent() {
        return smallestComponent;
    }
    
    public Integer getComponentValue(DateComponent dc) {
        switch(dc) {
            case YEAR:
                return year;
            case MONTH:
                return month;
            case DAY:
                return day;
            case HOUR:
                return hour;
            case MIN:
                return min;
            case SEC:
                return seg;
            default:
                return week;
        }
    }
    public Integer getFractionValue() {
        return frac;
    }
    public int getNumberOfFracDigits() {
        return numberOfFracDigits;
    }
    
    @Override
    public String toString() {
        if (week != null)
            return "P"+week+((DateComponent.WEEK.equals(smallestComponent) && (frac != null))?"."+frac:"")+"W";
        StringBuilder sb = new StringBuilder("P");
        if (year != null) sb.append(year+((DateComponent.YEAR.equals(smallestComponent) && (frac != null))?"."+strFrac(frac):"")+"Y");
        if (month != null) sb.append(month+((DateComponent.MONTH.equals(smallestComponent) && (frac != null))?"."+strFrac(frac):"")+"M");
        if (day != null) sb.append(day+((DateComponent.DAY.equals(smallestComponent) && (frac != null))?"."+strFrac(frac):"")+"D");
        if ((hour != null) | (min != null) | (seg != null)) sb.append("T");
        if (hour != null) sb.append(hour+((DateComponent.HOUR.equals(smallestComponent) && (frac != null))?"."+strFrac(frac):"")+"H");
        if (min != null) sb.append(min+((DateComponent.MIN.equals(smallestComponent) && (frac != null))?"."+strFrac(frac):"")+"M");
        if (seg != null) sb.append(seg+((DateComponent.SEC.equals(smallestComponent) && (frac != null))?"."+strFrac(frac):"")+"S");

        return sb.toString();
    }
    
    private String strFrac(int i) {
        int n = numberOfDigits(i);
        StringBuilder sb = new StringBuilder();
        for (; n < numberOfFracDigits; n++)
            sb.append('0');
        sb.append(i);
        return sb.toString();
    }
    private static int numberOfDigits(int value) {
        int ndigits = 1;
        while ((value /= 10) != 0) {
            ndigits++;
        }
        return ndigits;
    }
}
